﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using Client.App.Models;
using Multiprocessing.Core.Algorithm;
using Multiprocessing.Core.Units;

namespace Client.App.Services
{
    public sealed class AlgorithmsLoader
    {
        #region Fields

        private const string AlgorithmFamilyElementName = "algorithmFamily";
        private const string AlgorithmFamilyAttributeName = "name";
        private const string AlgorithmAttributeName = "name";
        private const string AlgorithmElementName = "algorithm";
        private const string AlgorithmTypeAttributeName = "type";

        private static readonly Lazy<AlgorithmsLoader> Lazy = new Lazy<AlgorithmsLoader>(() => new AlgorithmsLoader());

        #endregion

        #region Properties

        public static AlgorithmsLoader Instance
        {
            get { return Lazy.Value; }
        }

        #endregion

        #region Constructors

        private AlgorithmsLoader()
        {
        }

        #endregion

        #region Methods

        public IEnumerable<AlgorithmFamily> GetAlgorithmFamilies(string path)
        {
            if (ReferenceEquals(path, null))
            {
                throw new ArgumentNullException("path");
            }
            XDocument doc = XDocument.Load(path);
            XElement root = doc.Root;
            if (ReferenceEquals(root, null))
            {
                throw new NullReferenceException();
            }
            IEnumerable<XElement> algorithmFamilies = root.Elements(AlgorithmFamilyElementName);
            return algorithmFamilies.Select(GetAlgorithmFamily);
        }

        private static AlgorithmFamily GetAlgorithmFamily(XElement familyElement)
        {
            if (ReferenceEquals(familyElement, null))
            {
                throw new ArgumentNullException("familyElement");
            }
            XAttribute nameAttribute = familyElement.Attribute(AlgorithmFamilyAttributeName);
            if (ReferenceEquals(nameAttribute, null))
            {
                throw new NullReferenceException();
            }
            AlgorithmFamily family = new AlgorithmFamily(nameAttribute.Value);
            IEnumerable<XElement> algorithms = familyElement.Elements(AlgorithmElementName);
            foreach(XElement algorithmElement in algorithms)
            {
                family.Algorithms.Add(GetAlgorithm(algorithmElement));
            }
            return family;
        }

        private static IAlgorithm GetAlgorithm(XElement algorithmElement)
        {
            if (ReferenceEquals(algorithmElement, null))
            {
                throw new ArgumentNullException("algorithmElement");
            }
            XAttribute nameAttribute = algorithmElement.Attribute(AlgorithmAttributeName);
            if (ReferenceEquals(nameAttribute, null))
            {
                throw new NullReferenceException();
            }
            string name = nameAttribute.Value;
            XAttribute typeAttribute = algorithmElement.Attribute(AlgorithmTypeAttributeName);
            if (ReferenceEquals(typeAttribute, null))
            {
                throw new NullReferenceException();
            }
            Type type = Type.GetType(typeAttribute.Value);
            if (ReferenceEquals(type, null))
            {
                throw new NullReferenceException();
            }
            return (IAlgorithm) Activator.CreateInstance(type, ProcessingBlockFactory.Instance, name);
        }

        #endregion
    }
}
